using System;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.ViewModel;
using Microsoft.Practices.Unity;
using Ausy.Silverlight.Survey.Infrastructure.Common;
using Ausy.Silverlight.Survey.QuestionModule.Model;
using System.Collections.ObjectModel;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
using System.Linq;
using Ausy.Silverlight.Survey.Infrastructure.Events;

namespace Ausy.Silverlight.Survey.QuestionModule.ViewParts
{
    /// <summary>
    /// Cette classe représente le Vue Model pour le Question
    /// </summary>
    public class QuestionViewModel : ViewModel<IQuestionView>, IQuestionViewModel
    {
        #region Private Members

        /// <summary>
        /// Question a afficher
        /// </summary>
        private Question question;

        /// <summary>
        /// Temps de réponse de la quesion
        /// </summary>
        private TimeSpan ResponseTimeSpan;

        /// <summary>
        /// Token pour l'inscription a l'èvenement timeout
        /// </summary>
        private SubscriptionToken timerElapsedToken;

        /// <summary>
        /// Question en timeOut
        /// </summary>
        private bool isTimeout;

        #endregion

        #region Text Property
        /// <summary>
        /// Property text
        /// </summary>
        private string text;

        /// <summary>
        /// Property Text
        /// </summary>
        public string Text
        {
            get { return text; }
            set
            {
                if (text != value)
                {
                    text = value;
                    RaisePropertyChanged(() => this.Text);
                }
            }
        }

        #endregion

        #region EnabledQuestion Property
        /// <summary>
        /// Property enabledQuestion
        /// </summary>
        private bool enabledQuestion;

        /// <summary>
        /// Property enabledQuestion
        /// </summary>
        public bool EnabledQuestion
        {
            get { return enabledQuestion; }
            set
            {
                if (enabledQuestion != value)
                {
                    enabledQuestion = value;
                    RaisePropertyChanged(() => this.EnabledQuestion);
                }
            }
        }

        #endregion

        #region AnswerViewModelCollection Property
        /// <summary>
        /// Property answerViewModelCollection
        /// </summary>
        private ObservableCollection<IAnswerViewModel> answerViewModelCollection;

        /// <summary>
        /// Property AnswerViewModelCollection
        /// </summary>
        public ObservableCollection<IAnswerViewModel> AnswerViewModelCollection
        {
            get { return answerViewModelCollection; }
            set
            {
                if (answerViewModelCollection != value)
                {
                    answerViewModelCollection = value;
                    RaisePropertyChanged(() => this.AnswerViewModelCollection);
                }
            }
        }

        #endregion

        #region SelectedQuestion Property
        /// <summary>
        /// Property selectedQuestion
        /// </summary>
        private bool selectedQuestion;

        /// <summary>
        /// Property SelectedQuestion
        /// </summary>
        public bool SelectedQuestion
        {
            get { return selectedQuestion; }
            set
            {
                if (selectedQuestion != value)
                {
                    selectedQuestion = value;
                    RaisePropertyChanged(() => this.SelectedQuestion);
                    if (selectedQuestion)
                    {
                        this.OnQuestionSelected();
                    }
                }
            }
        }

        #endregion

        #region Response Property

        /// <summary>
        /// Résponse à la question
        /// </summary>
        public UserQuestionnaireResponse Response { get; private set; }

        #endregion

        #region public QuestionValidate Event

        /// <summary>
        /// Evènement lorsque la question est validée
        /// </summary>
        public event EventHandler<QuestionEventArgs> QuestionValidate;

        /// <summary>
        /// Evènement lorsque la question est validée
        /// </summary>
        private void OnQuestionValidate(QuestionEventArgs e)
        {
            EventHandler<QuestionEventArgs> handler = QuestionValidate;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region ValidateCommand Command Property

        /// <summary>
        /// Commande pour valider la question pour passer à la suivante
        /// </summary>
        public ICommand ValidateCommand { get; private set; }

        #endregion


        #region Constructor

        /// <summary>
        /// Constructeur par défaut
        /// </summary>
        /// <param name="container">Container Unity pour instanciation des type d'objet</param>
        public QuestionViewModel(IUnityContainer container)
            : base(container)
        {

        }

        #endregion

        #region Initialize Method

        /// <summary>
        /// Cette méthode permet l'initialisation du ViewModel avant que le DataContext soit affecté à la vue
        /// </summary>
        protected override void Initialize()
        {
            // Initialisation des données
            this.isTimeout = false;
            this.AnswerViewModelCollection = new ObservableCollection<IAnswerViewModel>();
            this.SelectedQuestion = false;
            this.ResponseTimeSpan = new TimeSpan(0);
            this.EnabledQuestion = true;

            // Initialisation des commandes
            this.ValidateCommand = new DelegateCommand<string>(this.OnValidate);
        }

        #endregion

        #region Load Method

        /// <summary>
        /// Cette méthode permet d'afficher une question
        /// </summary>
        /// <param name="question">question a afficher</param>
        public void Load(Question question)
        {
            if (question != null)
            {
                this.question = question;
                this.Text = this.question.Text;

                foreach (var answer in this.question.AnswerCollection)
                {
                    var answerViewModel = this.container.Resolve<IAnswerViewModel>();
                    answerViewModel.Load(answer);
                    this.AnswerViewModelCollection.Add(answerViewModel);
                }

                //Instansiation de la réponse pour l'utilisateur
                this.Response = new UserQuestionnaireResponse() { QuestionReferenceID = this.question.Identifier };
                this.Response.TotalResponseCounter = this.question.AnswerCollection.Count();
            }
        }

        #endregion

        #region OnQuestionSelected Method

        /// <summary>
        /// Question Sélectionnée et démarrage de la réponse
        /// </summary>
        public void OnQuestionSelected()
        {
            this.timerElapsedToken = this.eventAggregator.GetEvent<TimerElapsedEvent>().Subscribe(this.OnTimeout);
            this.Response.TimeSpanComplete = new TimeSpan();
            this.eventAggregator.GetEvent<TimerStartEvent>().Publish(new TimerStartEvent());
        }

        #endregion

        #region UpdapeResponse Method

        /// <summary>
        /// Cette méthode met à jour les réponses dans le UserQUestionnaireResponse
        /// </summary>
        private void UpdapeResponse()
        {
            if (!this.isTimeout)
            {
                this.Response.CorrectResponseCounter = 0;

                // Aucune réponse cochée ?
                var emptyResponse = this.AnswerViewModelCollection.All(a => !a.CheckedAnswer);

                // Ajoute les identifiants des réponses cochées
                foreach (var item in this.AnswerViewModelCollection)
                {
                    if (item.CheckedAnswer)
                    {
                        this.Response.ResponseAnswerCollection.Add(item.Answer.Identifier);
                    }

                    // Calcule des points
                    if (!emptyResponse && ((item.Answer.IsCorrect && item.CheckedAnswer) || (!item.Answer.IsCorrect && !item.CheckedAnswer)))
                    {
                        this.Response.CorrectResponseCounter++;
                    }
                }
                this.Response.TimeSpanComplete = this.ResponseTimeSpan;
            }
        } 

        #endregion

        #region OnTimeout Method

        /// <summary>
        /// Cette méthode traite le timeout
        /// </summary>
        /// <param name="args">argument de timeOut</param>
        public void OnTimeout(TimerElapsedEvent args)
        {
            if (args != null)
            {
                this.EnabledQuestion = false;
                this.ResponseTimeSpan = new TimeSpan(0, 0, 10);

                this.UpdapeResponse();

                // Question en Time Out
                this.isTimeout = true;
                this.eventAggregator.GetEvent<TimerElapsedEvent>().Unsubscribe(this.timerElapsedToken);
            }

        }

        #endregion

        #region OnValidate Method

        /// <summary>
        /// Cette méthode permet de valider la question
        /// </summary>
        /// <param name="arg">empty</param>
        public void OnValidate(string arg)
        {
            var args = new TimerStopEvent();
            args.ReturnAction = (s) =>
            {
                // s est le temps qu'il a été répondu
                this.ResponseTimeSpan = s;

                this.UpdapeResponse();

                this.OnQuestionValidate(new QuestionEventArgs());
            };
            
            // Arrete le timer et récupere le temps de réponse grace au delegate ReturnAction();
            this.eventAggregator.GetEvent<TimerStopEvent>().Publish(args);
          
        }

        #endregion
    }
}
